from resource import *
from commands import *
from views import *
from gamestate import GameState
from vec2d import vec2d
import glob
import random
import pygame.mixer_music
import imp

from vec2d import *
def signum(int):
    if(int < 0): return -1
    elif(int > 0): return 1
    else: return 0

quantum_duration=15     # duraction of a IA resolution in miliseconds, it controle the game speed
rotationperquantum=2   #3 per quantum 3600 per 1.2s
movepenality1=100        #1s to move
movepenality2=140        #s to move
visionapearture=46
reloadingpenality=[20,30,5,30,30]
objectradius=.40
uzidamage=50
maxhealth=100
spawnpenality=60
blastradius=3
blastdamage=200
rocketspeed=10

class PlayerRules:
    maxammo=[12*3,20,100,20,5]
    maxarmour=100

"""def GetListOfCoord(p,d):
    
    p0=vec2d(p)
    p1=p+d
    
    coord0=(int(math.floor(p0.x)),int(math.floor(p0.y)))
    coord1=(int(math.floor(p1.x)),int(math.floor(p1.y)))
    r=list()
    #No more intersection
    while coord0!=coord1:
        corner=vec2d(coord0[0]+(d.x>0),coord0[1]+(d.y>0))    
        det=d.cross(corner-p)    
        vertical=((d.x>0)==(d.y>0))==(det>0)
        if vertical:
            p0.y=p0.y+d.y*(corner.x-p0.x)/d.x
            p0.x=corner.x
            coord0=(coord0[0]+signum(d.x),coord0[1])            
        else:
            p0.x=p0.x+d.x*(corner.y-p0.y)/d.y
            p0.y=corner.y
            coord0=(coord0[0],coord0[1]+signum(d.y))
        
        r.append(coord0)        
        
    return  r
    """
class Interactive:
    def InteractWith(self,player):
        pass

class BulletImpactC(AbstractControler):
    def Update(self):
        if self.model.elapsed!=self.model.total:
            self.model.elapsed+=1 
        else:
            self.model.Destroy()
            
class AbstractTransientModel(AbstractModel,AbstractObservable):
    def __init__(self,controler):
        """ Transient object are used when an object is created in the simulation
        a transient objectis part of the map and can self destruct. It the iavisibile 
        is not specified, the object become invisible for the players"""
        AbstractModel.__init__(self, controler)
        AbstractObservable.__init__(self)     
        
    def Notify(self,observer):
        observer.OnDestruction(self)
    def Destroy(self):
        self.NotifyObservers()

class AbstractIngameModel(AbstractTransientModel):     
    def __init__(self,controler,iavisible):
        AbstractTransientModel.__init__(self,controler)
        self.iavisible=iavisible
        self.coord=0

class TransientPrototypeC(AbstractControler):
    def Update(self):
        if self.model.elapsed!=self.model.total:
            self.model.elapsed+=1             
        if self.model.remaininglife:
            self.model.remaininglife-=1
        else:
            self.model.Destroy()
 
class TransientPrototypeM(AbstractTransientModel):
     def __init__(self,pos,angle,prototype,controler=0):
        if controler==0: controler=TransientPrototypeC(self)
        AbstractTransientModel.__init__(self,controler)
        self.pos=vec2d(pos)
        self.angle=angle
        self.remaininglife=prototype.destruction
        self.total=prototype.duration
        self.elapsed=0
        self.prototype=prototype        
        GetGame().PlaySound(prototype.sound)        
 
     def _GetViewClass(self,contextId):
        return TransientPrototypeV

class BulletImpactM(AbstractTransientModel):
    def __init__(self,p0,p1,prototype):
        AbstractTransientModel.__init__(self,BulletImpactC(self))
        self.pos=p1
        self.p1=p0
        self.total=prototype.duration
        self.elapsed=0
        self.prototype=prototype
        self.angle=0
        
    def _GetViewClass(self,contextId):
        return BulletImpactV
    
class PlayerC(AbstractControler):
    def GiveHealth(self,hp):
        if self.model.hp==maxhealth:
            return False #it is not possible to heal the player
        else:
            if self.model.hp+hp<maxhealth:
                GetGame().PlaySound(self.model.prototype.healsound)
            
            self.model.hp=min(self.model.hp+hp,maxhealth)
            self.model.NotifyViews(ModelUpdateNotif())            
            return True
    def GiveWeapon(self,wn,an):
        m=self.model
        if m.weapons[wn] and m.ammo[wn]==m.maxammo[wn]:
            return False
        else:
            m.ammo[wn]=min(m.ammo[wn]+an,m.maxammo[wn])
            m.weapons[wn]=True 
            self.model.NotifyViews(ModelUpdateNotif())           
            return True
    def GiveAmmo(self,wn,an):
        m=self.model
        if m.ammo[wn]==m.maxammo[wn]:
            return False
        else:
            m.ammo[wn]=min(m.ammo[wn]+an,m.maxammo[wn])
            self.model.NotifyViews(ModelUpdateNotif())
            return True
    
    def GiveArmour(self,an):
        m=self.model
        if m.armour==m.maxarmour:
            return False
        else:
            m.armour=min(m.armour+an,m.maxarmour)
            self.model.NotifyViews(ModelUpdateNotif())
            return True
            
    def __init__(self,model):
        AbstractControler.__init__(self,model)
        self.notificationstack=list()
    def DelayedNotification(self,callable,args):
        self.notificationstack.append((callable,args))        
    def Hurt(self,damage):
        m=self.model
        map=GetGame().map
        self.DelayedNotification(self.model.iaplayer.OnWound,())        
        self.model.hp-=damage
        
        if damage>30:
            pId=random.choice(m.prototype.bloods)
            proto=GetGame().prototypes[pId]
            map.controler.AddTransientObject(TransientPrototypeM(m.fcoord*64,m.angle,proto))
            
        if self.model.hp<=0:            
            if damage>80:
                #explode !
                pvs=list(map.GetPVS(m.coord))
                for pId in m.prototype.gibs:
                    proto=GetGame().prototypes[pId]
                    d=random.choice(pvs).coord*64+(32,32)
                    a=random.uniform(-10,10)
                    map.controler.AddTransientObject(GibsM(m.fcoord*64,d,a,proto))
            else:                
                self.DelayedNotification(self.model.iaplayer.OnDeath,())
                proto=GetGame().prototypes[self.model.prototype.death]
                map.controler.AddTransientObject(TransientPrototypeM(self.model.fcoord*64,self.model.angle,proto))    
            self.model.Destroy()                
            return 1
        else:
            GetGame().PlaySound(self.model.prototype.hurtsound)
        return 0
    def Spawn(self):
        self.DelayedNotification(self.model.iaplayer.OnSpawn,())        
    def NotifyIA(self): 
        for callable,args in self.notificationstack:
            callable(*args)
        self.notificationstack=list()
        #final notification
        self.model.iaplayer.OnTick();

    def __FireBullet(self,dir):    
       map=GetGame().map                    
       p0=self.model.fcoord
       impact=map.GetImpactFromPos(p0,dir)
       p1=impact[0]*64
       p0=p0*64
       p0+=vec2d(self.model.prototype.gunoffset).rotated(self.model.angle)
        
       if impact[1]=="player" :
           proto=GetGame().prototypes['bullet-player-impact']
           map.controler.AddTransientObject(BulletImpactM(p0,p1,proto))            
           if impact[2].controler.Hurt(uzidamage)==1:
               self.model.team.controler.IncrementFrag(1)
           self.model.team.controler.IncrementShotHitEnnemy()                
       else:
           proto=GetGame().prototypes['bullet-wall-impact']
           map.controler.AddTransientObject(BulletImpactM(p0,p1,proto))
           self.model.team.controler.IncrementShotMiss()    
    def Update(self):        
        SomeThingDone=False     
        m=self.model
        if self.model.reloading>0:
            SomeThingDone=True
            self.model.reloading-=1
            if self.model.reloading==0:
                self.DelayedNotification(self.model.iaplayer.OnReloaded,())        
        elif m.firequerry:
            SomeThingDone=True
            m.firequerry=0
            weapon=m.firequerry_weapon
            m.firequerry_weapon=0
            if m.weapons[weapon]:
                if m.ammo[weapon]:
                    self.model.reloading=reloadingpenality[weapon]
                    #update stats
                    m.ammo[weapon]-=1
                    self.model.NotifyViews(ModelUpdateNotif())
                    #fire effet and calculus
                    if weapon==0:
                        GetGame().PlaySound('pistol-sound')
                        self.__FireBullet(self.model.GetDirection())
                    elif weapon==2:
                        GetGame().PlaySound('mgun-sound')                        
                        self.__FireBullet(self.model.GetDirection())                    
                    elif weapon == 1:
                        GetGame().PlaySound('shootgun-sound')                        
                        dir=self.model.GetDirection()
                        for d in [dir.rotated(a) for a in range(-3,4,1.5)]:
                            self.__FireBullet(d)
                    elif weapon == 4:
                       GetGame().PlaySound('rpg-sound')                       
                       map=GetGame().map                    
                       p0=self.model.fcoord
                       dir=self.model.GetDirection()
                       impact=map.GetImpactFromPos(p0,dir,True)
                       p1=impact[0]                       
                       map.controler.AddTransientObject(RocketM(p0,p1,self.model.angle,m))
                else:
                    #no ammo
                    self.model.reloading=20
                    
        if self.model.targetangle!=self.model.angle:
            SomeThingDone=True
            if abs(self.model.targetangle-self.model.angle)<rotationperquantum:
                self.model.angle=self.model.targetangle
            else:
                self.model.angle+=signum(self.model.targetangle-self.model.angle)*rotationperquantum
                                
            if self.model.targetangle==self.model.angle:
                #notify the IA OnAngleReached
                self.DelayedNotification(self.model.iaplayer.OnAngleReached,()) 
                pass
        
        # Movement handling
        if self.model.coord!=self.model.targetcoord:         
            SomeThingDone=True
            if self.model.moving>0:
                self.model.moving-=1
            if self.model.moving==0:
                #he target hasben reached
                self.model.coord=self.model.targetcoord
                self.DelayedNotification(self.model.iaplayer.OnPositionReached,())
                objs=GetGame().map.GetObjectsAt(self.model.coord)
                for o in objs:
                    if isinstance(o.controler,Interactive):
                        o.controler.InteractWith(self)
             
        if not SomeThingDone:
            self.DelayedNotification(self.model.iaplayer.OnIdle,())
            
class PlayerPublicIAC(AbstractControler):
    @property
    def fcoord(self):
        """object position (x,y)"""
        return self.model.fcoord
    @property
    def coord(self):
        """object coordinates (x,y)"""
        return self.model.coord
    @property
    def angle(self):
        """object coordinates (x,y)"""
        return self.model.angle
    @property
    def type(self):
        """type of object"""
        return """player"""
        

        
class PlayerProtectedIAC(AbstractControler):
    __slots__=[]    
    resourcemanager=GetApplication().resourcemanager
    def GetTargetPosition(self):        return self.model.targetcoord
    
    @property
    def weapons(self):               
        return tuple(self.model.weapons)
    @property
    def ammos(self):                 
        return tuple(self.model.ammo)
    @property
    def coord(self):              
        return tuple(self.model.coord)
    @property
    def fcoord(self):              
        return tuple(self.model.fcoord)
    def GetTargetAngle(self,angle):     return self.model.targetangle
    @property
    def angle(self):                 
        """Returns the current angle of the player"""
        return self.model.angle
    def GetHealtPoint(self):            return self.model.hp
    def Fire(self,weapon):              
        self.model.firequerry=1
        self.model.firequerry_weapon=weapon
    def GetMapSize(self):               return GetGame().map.size
    
    @property
    def targetcoord(self):              return tuple(self.model.targetcoord)
    @property
    def iswalking(self):                
        """ return true if the player is moving
            return false if the player is static"""
        return self.model.coord!=self.model.targetcoord       
    def SetTargetCoord(self,coord):
        directions={( 1, 0):[( 1, 0)],
                    ( 0, 1):[( 0, 1)],
                    (-1, 0):[(-1, 0)],
                    ( 0,-1):[( 0,-1)],
                    ( 1, 1):[( 1, 0),( 0, 1),( 1, 1)],
                    ( 1,-1):[( 0, 1),( 0,-1),( 1,-1)],
                    (-1, 1):[(-1, 0),( 0, 1),(-1, 1)],
                    (-1,-1):[( 0,-1),( 0,-1),(-1,-1)]                    
                    }
        model=self.model
        if model.moving:
            #The player is already moving!
            return False                      
        #get the coordinates in handy variables
        coord=(int(coord[0]),int(coord[1]))
        
        d=(coord[0]-model.coord[0],coord[1]-model.coord[1])
        if not d in directions:
            return False
        
        iswalk=GetGame().map.IsWalk            
        for dx,dy in directions[d]:
            if not iswalk((model.coord[0]+dx,model.coord[1]+dy)):
                return False
                        
        model.targetcoord=coord
        model.movepenality= model.moving= movepenality2 if dx and dy else movepenality1        
        return True
            
    def SetTargetAngle(self,angle):     self.model.targetangle=int(angle)
    def GetView(self):                  
        """Return the list of map cells that are in the view feild of the player.
        a sequence of CellPublicC is returned."""
        return self.model.GetView()
    
    def GetObjects(self):
        """Return the list of objects that are in the view feild of the player.
        a sequence of object is returned."""        
        return self.model.GetObjects()
    
class PlayerM(AbstractIngameModel,PlayerRules):
    def __init__(self,team,prototype,coord,ia):
        AbstractIngameModel.__init__(self,PlayerC(self),PlayerPublicIAC(self))
        self.dead=0
        #visu
        self.team=team
        self.prototype=prototype    
        #position model
        self.coord=(coord[0],coord[1])
        self.targetcoord=self.coord
        self.moving=0
        self.movepenality=1 #overall move penality for interpolation
        #angle model
        self.angle=0
        self.targetangle=self.angle
        #        
        self.hp=maxhealth
        #iaplayer
        self.iacontroler=PlayerProtectedIAC(self)        
        self.iaplayer=ia.SpawnIAPlayer(self.iacontroler)
        self.reloading=0
        self.firequerry=0        
        self.controler.Spawn()
        self.team.controler.IncrementSpawn()
        self.weapons=[1,0,0,0,0]
        self.ammo=[12,0,0,0,0]
        self.armour=0
        
    @property
    def fcoord(self):
        return vec2d(self.coord).interpolate_to(self.targetcoord,float(self.movepenality-self.moving)/self.movepenality)+(.5,.5)
    
    def GetDirection(self):
        return vec2d(math.cos(math.radians(self.angle)),math.sin(math.radians(self.angle)))
          
    def GetView(self):     
        d1=(math.cos(math.radians(self.angle-visionapearture)),math.sin(math.radians(self.angle-visionapearture)))
        d2=(math.cos(math.radians(self.angle+visionapearture)),math.sin(math.radians(self.angle+visionapearture)))
        
        view=GetGame().map.GetPVS(self.coord)
        cross=vec2d.cross
        def ViewFilter(cell):
            d=vec2d(cell.coord)-self.coord
            return cross(d,d1)<0 and cross(d,d2)>0
        return filter(ViewFilter,view)
    
    def GetObjects(self):
        d1=(math.cos(math.radians(self.angle-visionapearture)),math.sin(math.radians(self.angle-visionapearture)))
        d2=(math.cos(math.radians(self.angle+visionapearture)),math.sin(math.radians(self.angle+visionapearture)))
        cross=vec2d.cross
        m=GetGame().map
        pvs=m.GetPVS(self.coord)
        def ObjectFilter(obj):
            if not isinstance(obj,AbstractIngameModel): return False
            if not obj.iavisible: return False
            d=vec2d(obj.coord)-self.coord
            return cross(d,d1)<0 and cross(d,d2)>0 and m.map[obj.coord[0]][obj.coord[1]].iacontroler in pvs
        return map(lambda o:o.iavisible,filter(ObjectFilter,m.transientobjects))
        
    def _GetViewClass(self,contextId):
        if contextId!=DebugPaneContextId:
            return PlayerV
        else:
            return PlayerCornerV
  
    
    
class TeamC(AbstractControler):
    def __init__(self,model):
        AbstractControler.__init__(self,model)
    
    def SpawnAPlayer(self):        
        item=random.choice(self.model.spawnpoints)        
        prototype=GetGame().prototypes[self.model.prototype.player]
        player = PlayerM(self.model,prototype,item,self.model.abstractia)
        self.model.players.append(player)
        
        player.Register(self)
        GetGame().map.controler.AddTransientObject(player)
        
        proto=GetGame().prototypes['spawn-fx']
        obj=TransientPrototypeM(player.fcoord*64,0,proto)
        GetGame().map.controler.AddTransientObject(obj)
        
        self.model.NotifyViews(ModelUpdateNotif())
        return
        LogAbend("Unable to find de player prototype for the team %d"%self.model.prototype.team)
    
    def IncrementSpawn(self):          
        self.model.nbspawn+=1
        self.model.NotifyViews(StatUpdateNotif())
    def IncrementFrag(self,qtt):          
        self.model.nbfrag+=qtt
        self.model.NotifyViews(StatUpdateNotif())
    def UpdateShot(self):
        self.model.shot=self.model.nbshotmiss+self.model.nbshothitallie+self.model.nbshothitennemy
        if self.model.shot!=0: 
            self.model.precision= 100*self.model.nbshothitennemy/self.model.shot
        else:
            self.model.precision=0
    
    def IncrementShotHitAllie(self):  
        self.model.nbshothitallie+=1
        self.UpdateShot()
        self.model.NotifyViews(StatUpdateNotif())
    def IncrementShotHitEnnemy(self):  
        self.model.nbshothitennemy+=1
        self.UpdateShot()
        self.model.NotifyViews(StatUpdateNotif())
    def IncrementShotMiss(self):      
        self.model.nbshotmiss+=1
        self.UpdateShot()
        self.model.NotifyViews(StatUpdateNotif())
    
    def NotifyIA(self):        
        for player in self.model.players:
            player.controler.NotifyIA()
    
    def Update(self):
        if not self.model.players:
            if not self.model.spawndelay:
                self.SpawnAPlayer()
                self.model.spawndelay=spawnpenality
            else:
                self.model.spawndelay-=1
        else:
            for player in self.model.players:
                player.controler.Update()
    
    def OnDestruction(self,object):
        self.model.players.remove(object)
        self.model.NotifyViews(ModelUpdateNotif())
         
class TeamM(AbstractModel):    
    def __init__(self,IA,prototype,game):
        AbstractModel.__init__(self,TeamC(self))        
        self.prototype=prototype  
        self.game=game
        self.abstractia=IA       
        self.spawnpoints=list()
        self.players=list()
        self.spawndelay=0        
        #stat
        self.nbspawn=0           
        self.nbfrag=0 
        self.nbshothitennemy=0
        self.nbshothitallie=0
        self.nbshotmiss=0
        self.nbmove=0
        self.shot=0
        self.precision=0        
        self.face=FaceM(prototype)
    
        
    def AddSpawnPoint(self,sp):
        self.spawnpoints.append(sp)
    def _GetViewClass(self,contextId):
        return TeamCornerV
        
    
class GameC(AbstractControler):
    def __init__(self,model):
        AbstractControler.__init__(self,model)  
        self.current_quantum=0        
    def NewMap(self):        
        self.SetGameState(GameState.stoped)
        self.model.map.controler.Reset()
        pass
    def LoadMap(self,filename):
        self.SetGameState(GameState.stoped)
        self.model.map.controler.Reset()
        for line in open(filename,"r").read().splitlines():
            eval(line)
        pass
    def SaveMap(self,filename):
        out=open(filename,"w+")
        for r in self.model.map.map:
            for c in r:
                out.write("self.model.map.map[%d][%d].controler.SetPrototype(self.model.prototypes['%s'])\n"%(c.coord[0],c.coord[1],c.prototype.id))
        for o in self.model.map.objects:            
                out.write("self.model.map.controler.AddObjectProto((%d,%d),self.model.prototypes['%s'])\n"%(o.coord[0],o.coord[1],o.prototype.id))
        pass
    def SetGameState(self,state):
        if self.model.state==GameState.stoped and state in [GameState.playing,GameState.paused]:
            self.__BeginGame()
        elif self.model.state in [GameState.playing,GameState.paused] and state==GameState.stoped:
            self.__EndGame()
        self.model.state=state
        pass                        
    def __EndGame(self):
        #create the teams  
        self.model.PlayMusic(None)
        self.model.teams=dict()        
        self.model.map.controler.RemoveAllTransientObjects()
        self.model.NotifyViews(SwitchContextIdNotif(DefaultContextId))
        
    def __BeginGame(self):
        model=self.model
        if not IsPydevDebugMode():
            #avoid this long operation in debug mode
            model.map.DelayedComputeFullPVS()
            
        #create the teams
        model.PlayMusic('ambience')
        self.RemoveAllTeams()
        classlist=list(model.IALibrary)
        for obj in model.map.objects:            
            if obj.prototype.type=="object":                
                model.map.controler.AddTransientObject(GoodM(model.map,obj.coord,obj.prototype))
            if obj.prototype.type=="spawn":
                teamprototype=model.prototypes[obj.prototype.team]
                #Create the teams according to swpawn points
                if not[t for t in model.teams if t.prototype==teamprototype]:
                    #Get the correponding module
                    if len(classlist)==0:                     
                        LogAbend("Not enough ia has been published to play on this map")
                        sys.exit(0)
                    
                    class_=classlist.pop()                    
                    try:
                        IA=class_()                   
                    except TypeError:
                        print "An error occured while creating the class '%s'"%class_
                        print "The __init__ signature should take only one argument"                       
                        
                    #Create the team object
                    self.AddTeam(TeamM(IA,teamprototype,model))
                #register the spawn point
                for t in model.teams:
                    if t.prototype==teamprototype:
                        t.AddSpawnPoint(obj.coord)
                        break
            
        model.NotifyViews(SwitchContextIdNotif(SimulationContextId))
        
        
    def AddTeam(self,team):
        for t in self.model.teams:
            if t.prototype==team.prototype:
                return
        self.model.teams.append(team)
    def RemoveAllTeams(self):        
        self.model.teams=list()                
    def Update(self):                
        #Queue management before all
        if self.model.DelayedTasksQueue:
            #process tasks durring 100 milisec
            current_time = pygame.time.get_ticks()
            while pygame.time.get_ticks()-current_time<100 and self.model.DelayedTasksQueue:
                self.model.DequeueOneTask()                
        else:            
            for i in xrange(1,10):
                current_time = pygame.time.get_ticks()
                if self.current_quantum*quantum_duration>current_time:
                    return #Time has been handeled corectly
                self.UpdateIAQuantum()
                self.current_quantum+=1
        
        quantum = current_time/quantum_duration
        print "IA frame resolution skipping: %d -> %d"%(self.current_quantum,quantum)
        self.current_quantum = quantum
        
    def UpdateIAQuantum(self):
        #Game management     
        if self.model.state==GameState.playing:            
            #Notification step
            for team in self.model.teams:
                team.controler.NotifyIA()
            #Resolution step
            for team in self.model.teams:
                team.controler.Update()
            self.model.map.controler.Update()
        
        #Toolbars scrolling management
        for t in self.model.toolpalettes:
            if isinstance(t.controler,AbstractDynamicControler):
                t.controler.Update()
        pass
                
class Prototype:
    def LoadRessources(self):
        surfaces=map(GetApplication().resourcemanager.Load, self.resources)
        if hasattr(self,"colorkey"):
            for s in surfaces:
                if s.get_colorkey()!=self.colorkey:
                    s.set_colorkey(self.colorkey)
        return surfaces

class GameMTaskQueue:
    def __init__(self):
        self.DelayedTasksQueue=list()
        self.NbDelayedTasks=0
    
    def QueueDalayedTask(self,fn,args):
        self.DelayedTasksQueue.append((fn,args))
        self.NbDelayedTasks+=1
        
    def DequeueOneTask(self):
        if self.DelayedTasksQueue:
            fn,args=self.DelayedTasksQueue.pop()
            fn(*args)
            if not self.DelayedTasksQueue:
                self.NbDelayedTasks=0
    

class GameM(AbstractModel,GameMTaskQueue):
    def __init__(self):
        GameMTaskQueue.__init__(self)
        AbstractModel.__init__(self,GameC(self))        
        #Build prototype dictionnary        
        self.prototypes=dict()
        config=GetApplication().resourcemanager.Load("prototypes.cfg")        
        for section in config.sections():             
            args=Prototype() 
            args.id=section
            for key, value in config.items(section): 
                setattr(args, key, eval(value))        
            self.prototypes[section]=args       
            
        #Common data
        self.state=GameState.stoped        
        self.map=MapM(self)  

        self.teams=[]        
        self.toolpalettes=[ ToolPaletteM(p,self) for p in self.prototypes.values() if p.type=='toolpalette']
        self.toolpalettes+=[ ScrollPaletteM(p,self) for p in self.prototypes.values() if p.type=='scrollpalette']
        self.IALibrary=[]
        self.ImportIAModules()
        
            
    def LoadResources(self):
        #Load all resources
        print "===== Load all resources ====="
        for proto in self.prototypes.values():
            if not hasattr(proto,"resources"):
                print "WARNING: The prototype '%s' has not resources key."%proto.id
                continue
            load=GetApplication().resourcemanager.Load
            for resId in proto.resources:
                self.QueueDalayedTask(load,(resId,))
        
        #Load the default file
        print "===== Load default map   ====="
        if os.path.isfile("./maps/default.map"):
            self.controler.LoadMap("./maps/default.map")
        
    def PlaySound(self,protoId):
        """Play a sound music form a prototype. If None is passed nothing is done"""
        app=GetApplication()      
        if not protoId:
            return
        else:
            resId= random.choice(self.prototypes[protoId].resources)        
            sound=app.resourcemanager.Load( resId )
            app.PlaySound(sound)
                
    def PlayMusic(self,protoId):
        """Play a music form a prototype. If None is passed the current music fades out and stop"""
        app=GetApplication()              
        if not protoId:
            app.MusicFadeOut(2000)
        else:
            resId= random.choice(self.prototypes[protoId].resources)
            music=app.resourcemanager.Load( resId )
            app.PlayMusic(music)        
            
        
    def ImportIAModules(self):
        print "===== Load IA modules    ====="
        pluginpath="ia"        
        if not pluginpath in sys.path:
            sys.path.append(pluginpath)
            
        self.IAModules = list()
        for iamodule in glob.glob(os.path.join(pluginpath,"*.py")):            
            modulename= os.path.splitext( os.path.basename(iamodule))[0]
            module=__import__(modulename)
            print "IA module found '%s'"%modulename,module.__dict__["exported"]
            self.IALibrary+=module.exported
    def _GetViewClass(self,contextId):
        return GameV


gameinstance = None
def GetGame():
    global  gameinstance
    if gameinstance is None:
       gameinstance = GameM()
       gameinstance.LoadResources()
    return gameinstance
class CellPublicC(AbstractControler):
    __slots__=[]
    @property
    def solid(self):
        """True when the cell stops bullets and projectiles"""
        return not not self.model.prototype.solid
    @property
    def transparent(self):
        """True when is is possible to see through the cell"""
        return not not self.model.prototype.transparent
    @property
    def walk(self):
        """True when is is possible to walk through the cell"""
        return not not self.model.prototype.walk
    @property
    def coord(self):
        """return (x,y), a tuple with the coordinates of the cell
        x axis is from left to right
        y axis is from top to bottom"""
        return self.model.coord
    

class CellC(AbstractControler):
    def __init__(self,model):
        AbstractControler.__init__(self,model)   
    def SetPrototype(self, prototype):
        if prototype==0: 
            self.model.prototype=prototypes['default']
        else:
            self.model.prototype=prototype
        self.model.NotifyViews(ModelUpdateNotif()) 
    def ClearObject(self):
        self.model.objects=list()
        self.model.NotifyViews(ModelUpdateNotif())       
    #def AddObject(self, prototype):
    #    self.model.objects.append(ObjectM(self.model.pos, prototype))  
    #    self.model.NotifyViews(ModelUpdateNotif())       

class ObjectC(AbstractControler):
    def __init__(self,model):
        AbstractControler.__init__(self,model)  

class CellC(AbstractControler):
    def __init__(self,model):
        AbstractControler.__init__(self,model)           
    def SetPrototype(self, prototype):
        if prototype==0: 
            self.model.prototype=GetGame().prototypes['default']
        else:
            self.model.prototype=prototype
        GetGame().map.InvalidatePVS()
        self.model.NotifyViews(ModelUpdateNotif()) 
    def ClearObject(self):
        self.model.objects=list()
        self.model.NotifyViews(ModelUpdateNotif())       
    #def AddObject(self, prototype):
    #    self.model.objects.append(ObjectM(self.model.pos, prototype))  
    #    self.model.NotifyViews(ModelUpdateNotif())       
            
class CellM(AbstractModel):
    def __init__(self, map, coord, prototype):
        AbstractModel.__init__(self,CellC(self))
        self.iacontroler=CellPublicC(self)
        self.map=map
        self.coord=vec2d(coord)
        self.pos=self.coord*64+(32,32)
        self.prototype=prototype
        
    @property
    def solid(self):
        """True when the cell stops bullets and projectiles"""
        return not not self.prototype.solid
    @property
    def transparent(self):
        """True when is is possible to see through the cell"""
        return not not self.prototype.transparent
    @property
    def walk(self):
        """True when is is possible to walk through the cell"""
        return not not self.prototype.walk
    
    def _GetViewClass(self,contextId):
        if contextId==DefaultContextId: 
            return CellEditionV
        else:
            return CellSimulationV
        

    

class ObjectM(AbstractModel):
    def __init__(self, coord, prototype):
        AbstractModel.__init__(self) 
        self.coord=vec2d(coord)
        self.pos=self.coord*64+(32,32)
        self.prototype=prototype
        
    def _GetViewClass(self,contextId):
        return ModelPrototypeV    


class GoodC(AbstractDynamicControler,Interactive): 
    def Update(self):
        if self.model.remain!=0:
            self.model.remain-=1
            if self.model.remain==0:
               self.Spawn()
    def InteractWith(self,player):
        if self.model.remain==0:
            #run the script
            if eval(self.model.prototype.oninteract):
                GetGame().PlaySound(self.model.prototype.sound)                
            
    def Consume(self):
        #reload the spawn delay
        self.model.remain=self.model.prototype.spawndelay
        return 1
        
    def Spawn(self):
        proto=GetGame().prototypes['spawn-fx']
        obj=TransientPrototypeM(self.model.pos,0,proto)
        self.model.map.controler.AddTransientObject(obj)
class GoodPublicC(AbstractControler):
    __slots__=[]
    @property    
    def angle(self):
        return 0    
    @property    
    def fcoord(self):
        """object position (x,y)"""
        return self.model.coord+(.5,.5)
    @property
    def coord(self):
        """object coordinates (x,y)"""
        return self.model.coord
    @property
    def type(self):
        """type of object"""
        return self.model.prototype.id

class GoodM(AbstractIngameModel):    
    def __init__(self, map, coord, prototype):
        AbstractIngameModel.__init__(self,GoodC(self),GoodPublicC(self)) 
        self.coord=vec2d(coord)
        self.pos=self.coord*64+(32,32)
        self.prototype=prototype    
        self.remain=0
        self.map=map
    def _GetViewClass(self,contextId):
         return GoodV

#le modele agrege le modele sinon on s'en sort plus
class MapM(AbstractModel):
    def __init__(self,game,size=(18,10)):    
        AbstractModel.__init__(self,MapC(self))
        #build a empty arra of cell
        self.size=size
        p=game.prototypes['default']
        self.map=[[CellM(self,(x,y),p) for y in xrange(0,size[1])] for x in xrange(0,size[0])]        
        self.objects=[]
        
        self.pvs=0
        self.transientobjects=[]                
        self.InvalidatePVS()
        
    def _GetViewClass(self,contextId):
         return MapV
    
    def InvalidatePVS(self):
        self.pvs=[[None]*self.size[1] for x in xrange(0,self.size[0])]
        
    def DelayedComputeFullPVS(self):
        game=GetGame()        
        for x in xrange(0,self.size[0]):             
            for y in xrange(0,self.size[1]):                             
                game.QueueDalayedTask(self.GetPVS,((x,y),))
                

    def GetPVS(self,coord):
        x,y=coord
        v=self.pvs[x][y]
        if not v: self.pvs[x][y]=v=self.BuildCellPVS(coord)
        return v            
    def GetObjectsAt(self,coord):
        return [o for o in self.transientobjects if isinstance(o,AbstractIngameModel) and o.coord==coord]
                
    def BuildCellPVS(self,eye):
        r=list()
        for xmap in self.map:
            for iacontroler in xmap:
                if self.IsCellViewedFromCell(eye[0],eye[1],*iacontroler.coord):                    
                    r.append(iacontroler)
        return r
    
    def IsCellViewedFromCell(self,eyex,eyey,coordx,coordy): 
        if not self.IsTransparent((eyex,eyey)):
            return False
        #It is the same location, it is allays visible avoid 1/0
        if eyex==coordx and eyey==coordy:
            return True        
                
        p0x=eyex+.5 
        p0y=eyey+.5        
        if not self.IsTransparent((coordx,coordy)):                 
            p1x=coordx+(.05,.95)[eyex>coordx]
            p1y=coordy+.5            
            if self.IsPosViewedFromPos((p0x,p0y),(p1x,p1y)):                
                return True        
            
            p1x=coordx+.5            
            p1y=coordy+(.05,.95)[eyey>coordy]            
            if self.IsPosViewedFromPos((p0x,p0y),(p1x,p1y)):                
                return True            
            return False
        else:
            p1x=coordx+.5 
            p1y=coordy+.5       
        
            #direction perpendicular normal
            nx=coordy-eyey
            ny=-coordx+eyex
            #normailze
            ilenght=objectradius/math.sqrt(nx*nx + ny*ny)
            nx*=ilenght
            ny*=ilenght            
            if self.IsPosViewedFromPos((p0x,p0y),(p1x+nx,p1y+ny)):
                return True
            if self.IsPosViewedFromPos((p0x,p0y),(p1x-nx,p1y-ny)):
                return True
                 
    def IsSolid(self,coord):
        """return True if the cell at the given coordinate is solid
        It returns True if it is out of the map"""
        x,y=coord
        if x<0 or x>=self.size[0] or y<0 or y>=self.size[1]: return True            
        return self.map[x][y].solid
    
    def IsWalk(self,coord):
        """return True if it is possible to walk through the cell ay the given coordinates
        It returns True if the cell is out of the map"""        
        x,y=coord
        if x<0 or x>=self.size[0] or y<0 or y>=self.size[1]: return False
        return self.map[x][y].walk
            
    def IsTransparent(self,coord):
        """return True if it is possible to see through the cell ay the given coordinates
        It returns False if the cell is out of the map"""                
        x,y=coord
        if x<0 or x>=self.size[0] or y<0 or y>=self.size[1]: return False            
        return self.map[x][y].transparent
    
        
    def IsPosViewedFromPos(self,eye,pos):        
        """Expects integer vector containing the coordinates of the position and the direction"""    
        p0x=eye[0]       ;p0y=eye[1]
        p1x=pos[0]       ;p1y=pos[1]
        dx=p1x-p0x       ;dy=p1y-p0y
        coord0x=int(p0x) ;coord0y=int(p0y)
        coord1x=int(p1x) ;coord1y=int(p1y)
        
        #No more intersection
        while coord0x!=coord1x or coord0y!=coord1y:
            if not self.IsTransparent((coord0x,coord0y)):
                return False
            cornerx=float(coord0x+(dx>0))
            cornery=float(coord0y+(dy>0))
            det=dx*(cornery-p0y) - dy*(cornerx-p0x)
            
            vertical=((dx>0)==(dy>0))==(det>0)
            if vertical:
                #intersection with a vertial line
                p0y+=dy*(cornerx-p0x)/dx 
                p0x=float(cornerx) # x is projected on the line
                if dx>0: coord0x+=1
                else:    coord0x-=1
                  
            else:
                #intersection with a horizontal line
                p0x+=dx*(cornery-p0y)/dy
                p0y=float(cornery) # y is projected on the line
                if dy>0: coord0y+=1
                else:    coord0y-=1
                
        return True
    
    def GetImpactFromPos(self,p0,d,wallonly=0):        
        """Expects integer vector containing the coordinates of the position and the direction"""    
        p1=vec2d(p0)
        coord0=(int(p0[0]),int(p0[1]))
        #Compute the intersection with a wall
        while 1:            
            if self.IsSolid(coord0):
                break
            corner=vec2d(coord0[0]+(d.x>0),coord0[1]+(d.y>0))    
            det=d.cross(corner-p0)    
            vertical=((d.x>0)==(d.y>0))==(det>0)
            if vertical:
                p1.y=p1.y+d.y*(corner.x-p1.x)/d.x
                p1.x=corner.x
                coord0=(coord0[0]+signum(d.x),coord0[1])            
            else:
                p1.x=p1.x+d.x*(corner.y-p1.y)/d.y
                p1.y=corner.y
                coord0=(coord0[0],coord0[1]+signum(d.y))
        
        #Compute the intersection with a player
        if wallonly:
            return (p1,"wall",0)

        playerimpact=None
        u=p1-p0
        min=objectradius
        max=u.normalize_return_length()   
        v=u.perpendicular()
        for t in GetGame().teams:
            for p in t.players:
                rp=p.fcoord-p0
                cu=rp.dot(u)
                cv=rp.dot(v)
                if min<cu and cu<max and -objectradius<cv and cv<objectradius:
                    #Impact on a player detected
                    max=cu-objectradius*.5
                    p1=p0+u*max
                    playerimpact=p
        if playerimpact!=None:
            return (p1,"player",playerimpact)
        else:
            return (p1,"wall",0)

class RocketPublicC(AbstractControler):
    __slots__=[] 
    @property       
    def coord(self):
        return (int(self.model.pos[0]+.5),int(self.model.pos[1]+.5))
    @property    
    def fcoord(self):
        return self.model.pos
    @property
    def angle(self):
        return self.model.angle
    @property
    def type(self):
        return "rocket"
    
class RocketC(AbstractControler):
    def __Explode(self):
        m=self.model
        map=GetGame().map        
        
        #compute blast radius        
        for team in GetGame().teams:
            for player in team.players:
                playerfcoord=player.fcoord
                if(map.IsPosViewedFromPos(m.fcoord,playerfcoord)):
                    #blast possible
                    dist=m.fcoord.get_dist_sqrd(playerfcoord)
                    sqrtblastradius=blastradius*blastradius
                    if dist<sqrtblastradius:
                        #blast confirmated
                        damage=(sqrtblastradius-dist)*blastdamage/sqrtblastradius
                        if player.controler.Hurt(damage):
                            #death!
                            if player in m.emiter.team.players:
                                #colateral damage
                                m.emiter.team.controler.IncrementFrag(-1)
                            else:
                                m.emiter.team.controler.IncrementFrag(1)
                        
        proto=GetGame().prototypes['blast-fx']        
        obj=TransientPrototypeM(self.model.pos,0,proto)                
        map.controler.AddTransientObject(obj)
        m.Destroy()
        
    def Update(self):
        m=self.model
        if m.elapsed<m.total:
            m.elapsed+=1
            #periodicaly release smoke
            m.fcoord=m.fc0.interpolate_to(m.fc1,m.elapsed/float(m.total))            
            m.pos=m.fcoord*64
            if not m.elapsed%3:
                GetGame().map.controler.AddTransientObject(SmokeM(m.pos-m.emiterdir*10,m.emiterdir,((m.elapsed/3)%3-1)*5,m.smokeproto) )
            
            for t in GetGame().teams:
                for player in t.players:
                    if player!=m.emiter:
                        if m.fcoord.get_dist_sqrd(player.fcoord)<objectradius*objectradius:
                            # imapct !
                            self.__Explode()            
        else:
            self.__Explode()
           
class SmokeC(TransientPrototypeC):
    def Update(self):
        TransientPrototypeC.Update(self)
        self.model.angle+=self.model.angleincrement
        self.model.pos+=self.model.dir
        
class SmokeM(TransientPrototypeM):
    def __init__(self,pos,dir,angle,proto):        
        TransientPrototypeM.__init__(self,pos,0,proto,SmokeC(self))        
        self.dir=dir
        self.angle=0
        self.angleincrement=angle
    def _GetViewClass(self,contextId):
         return TransientPrototypeSpriteV

class GibsC(TransientPrototypeC):
    def Update(self):        
        TransientPrototypeC.Update(self)
        m=self.model
        m.angle=m.angle*.95+m.angletarget*.05
        m.pos=m.pos.interpolate_to(m.pos1,.05)        
class GibsM(TransientPrototypeM):
    def __init__(self,pos0,pos1,angle,proto):        
        TransientPrototypeM.__init__(self,pos0,0,proto,GibsC(self))        
        self.pos1=pos0.interpolate_to(pos1,random.uniform(.1,.5))
        self.angle=0
        self.angletarget=angle
    def _GetViewClass(self,contextId):
         return TransientPrototypeSpriteV
              
class RocketM(AbstractIngameModel):
    def __init__(self,fc0,fc1,a,e):
        self.smokeproto=prototype=GetGame().prototypes["smoke-fx"]
        AbstractIngameModel.__init__(self,RocketC(self),RocketPublicC(self))        
        self.fcoord=fc0
        self.pos=fc0*64
        self.fc0=fc0
        self.fc1=fc1
        self.total=int(fc0.get_distance(fc1)*rocketspeed)
        self.elapsed=0
        self.angle=a        
        self.emiterdir=(fc1-fc0).normalized()
        
        self.emiter=e
        self.prototype=GetGame().prototypes['rocket']
    def _GetViewClass(self,contextId):
         return TransientPrototypeSpriteV

class FaceM(AbstractModel):
    def __init__(self,prototype):
        AbstractModel.__init__(self, AbstractControler(self))
        self.prototype=prototype
        self.pos=(200,40)
    def _GetViewClass(self,contextId):
         return ModelPrototypeV
    
        
class MapC(AbstractControler):
    def __init__(self,model):
        AbstractControler.__init__(self,model)
    
    def AddTransientObject(self,o):    
        if isinstance(o,AbstractTransientModel):
            o.Register(self)  
        self.model.transientobjects.append(o)
        self.model.NotifyViews(AddObjectNotif(o))
    def RemoveTransientObject(self,object):
        self.model.transientobjects.remove(object)
        self.model.NotifyViews(RemoveObjectNotif(object))
    def RemoveAllTransientObjects(self):
        for o in self.model.transientobjects:
            self.model.NotifyViews(RemoveObjectNotif(o))
        self.model.transientobjects=list()
    def AddObjectProto(self,coord,prototype):
        self.AddObject(ObjectM(coord,prototype))       
    def AddObject(self,object):
        self.model.objects.append(object)        
        self.model.NotifyViews(AddObjectNotif(object))
    def Update(self):
        for o in self.model.transientobjects: 
            if o.controler==0:
                pass
            o.controler.Update()        
    def RemoveObject(self,object):
        self.model.objects.remove(object)
        self.model.NotifyViews(RemoveObjectNotif(object))
    def RemoveAllObjects(self):
        for o in self.model.objects:
            self.model.NotifyViews(RemoveObjectNotif(o))
        self.model.objects=[]      
    def Reset(self):
        self.RemoveAllObjects()
        self.RemoveAllTransientObjects()
        for i in self.model.map:
            for j in i:
                j.controler.SetPrototype(0)
        
    OnDestruction=RemoveTransientObject

if os.path.isdir(sys.path[0]):
    os.chdir(sys.path[0])
    print sys.path[0]
else:
    print sys.path[0]
    os.chdir(os.path.dirname(sys.path[0]))
app=GetApplication()
app.Run(GetGame())
